/* 
 * Copyright [2018] [Alex/libo(liboware@gmail.com)]
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.codex.shiro.config;
import java.util.HashMap;
import java.util.Map;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import com.hyts.codex.config.AuthConfiguration;
import com.hyts.codex.shiro.handler.CustomRealm;

import lombok.extern.slf4j.Slf4j;

/** 
 * @author LiBo/Alex
 * @see org.apache.shiro.spring.web.ShiroFilterFactoryBean
 * @since V1.0
 * @version V1.0 
 */
@Slf4j
@AutoConfigureAfter(AuthConfiguration.class)
@Configuration
//@Import(AuthConfiguration.class)
//@ComponentScan(basePackages="com.hyts.codex.shiro")
public class ShiroConfiguration {
    
    /**  
     * <b>子类覆盖的相关构建实现应用的构建相关map服务信息</b>
     * @return
     * @exception
     */ 
    /*public Map<String,String> filterChainDefinitionMapBuild(){
        
    }*/
    
    /**  
     * @fieldName DEFAULT_LOGIN_URL
     * @fieldType String
     */ 
    private static final String DEFAULT_LOGIN_URL = "/login";
    
    /**  
     * @fieldName DEFAULT_NO_AUTHOR_URL
     * @fieldType String
     */ 
    private static final String DEFAULT_SUCCESS_URL = "/index";
    
    /**  
     * @fieldName DEFAULT_LOGOUT_URL
     * @fieldType String
     */ 
    private static final String DEFAULT_LOGOUT_URL = "/logout";

    
    /**  
     * <p>游客访问路径无需权限认证，作为系统开放访问</p> 
     * @fieldName DEFAULT_VISTOR_URL
     * @fieldType String
     */ 
    public static final String DEFAULT_VISTOR_URL = "/access"; 
    
    /**  
     * @fieldName DEFAULT_THREE_URL
     * @fieldType String
     */ 
    public static final String DEFAULT_THREE_URL = "/stateless"; 
    
    
    public static final String DEFAULT_STATIC_URL = "/static"; 
    
    
    public static final String DEFAULT_HTML_URL = "/html"; 

    
    public static final String DEFAULT_JS_URL = "/js"; 

    
    public static final String DEFAULT_CSS_URL = "/css"; 
    
    
    public static final String DEFAULT_IMG_URL = "/images"; 
    
    /**  
     * <p>加密类型</p> 
     * @fieldName DEFAULT_SECRET_TYPE
     * @fieldType String
     */ 
    public static final String DEFAULT_SECRET_TYPE = "md5";
    
    /**  
     * <p>加密迭代次数</p> 
     * @fieldName DEFAULT_SECRET_NUM
     * @fieldType int
     */ 
    public static final int DEFAULT_SECRET_NUM = 2;
    
    /**  
     * <b>shiroFilter</b>
     * <p>shiroFilter核心代理对象，是所有的相关服务的总体核心控制器</p>
     * @param securityManager
     * @return
     * @exception
     */
    @ConditionalOnMissingBean(ShiroFilterFactoryBean.class)
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        log.info("开始构建相关服务初始化shiro服务的相关工作控制器......");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String,String> filterChainDefinitionMap = null;//filterChainDefinitionMapBuild();
        if(CollectionUtils.isEmpty(filterChainDefinitionMap)) {
           log.info("子类构造的映射关系为空，选择默认服务操作filterChainDefinitionMap对象");
           filterChainDefinitionMap = new HashMap<>();
           log.info("默认登录访问：{}",DEFAULT_LOGIN_URL);
           log.info("默认成功访问：{}",DEFAULT_SUCCESS_URL);
           log.info("默认注销访问：{}",DEFAULT_LOGOUT_URL);
           log.info("默认无需权限访问：{}",DEFAULT_VISTOR_URL);
           log.info("默认无需权限访问：{}",DEFAULT_THREE_URL);
           log.info("默认无需权限访问静态资源：{}",DEFAULT_HTML_URL);
           log.info("默认无需权限访问静态资源：{}",DEFAULT_JS_URL);
           log.info("默认无需权限访问静态资源：{}",DEFAULT_CSS_URL);
           log.info("默认无需权限访问静态资源：{}",DEFAULT_IMG_URL);

           shiroFilterFactoryBean.setLoginUrl(DEFAULT_LOGIN_URL);
           shiroFilterFactoryBean.setUnauthorizedUrl(DEFAULT_LOGIN_URL);
           shiroFilterFactoryBean.setSuccessUrl(DEFAULT_SUCCESS_URL);
           filterChainDefinitionMap.put(DEFAULT_LOGOUT_URL, "logout");
           filterChainDefinitionMap.put(DEFAULT_VISTOR_URL+"/**", "anon");
           filterChainDefinitionMap.put(DEFAULT_HTML_URL+"/**", "anon");
           filterChainDefinitionMap.put(DEFAULT_JS_URL+"/**", "anon");
           filterChainDefinitionMap.put(DEFAULT_CSS_URL+"/**", "anon");
           filterChainDefinitionMap.put(DEFAULT_IMG_URL+"/**", "anon");
           filterChainDefinitionMap.put(DEFAULT_THREE_URL+"/**", "anon");
           //filterChainDefinitionMap.put("/index2","roles[user]");
           filterChainDefinitionMap.put(DEFAULT_LOGIN_URL, "anon");
           //主要这行代码必须放在所有权限设置的最后，不然会导致所有 url 都被拦截
           filterChainDefinitionMap.put("/**", "authc");
           shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        }
        log.info("选择定制化服务操作filterChainDefinitionMap对象");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        log.info("构建shiro总体配置完成");
        return shiroFilterFactoryBean;
    }
    
    /**  
     * <b>默认shiro的相关配置-加密方式</b>
     * @return
     * @exception
     */ 
    @ConditionalOnMissingBean(HashedCredentialsMatcher.class)
    @Bean
    public HashedCredentialsMatcher hashCredentialsMatcher() {
        HashedCredentialsMatcher hashCredentialsMatcher = new HashedCredentialsMatcher();
        hashCredentialsMatcher.setHashAlgorithmName(DEFAULT_SECRET_TYPE);
        hashCredentialsMatcher.setHashIterations(DEFAULT_SECRET_NUM);
        return hashCredentialsMatcher;
    }
    
    /**  
     * @return
     * @exception
     */ 
    //@ConditionalOnMissingBean(AuthorizingRealm.class)
    @Bean
    public AuthorizingRealm accessRealm() {
        CustomRealm customRealm = new CustomRealm();
        customRealm.setCredentialsMatcher(hashCredentialsMatcher());
        return customRealm;
    }
    
    /**  
     * @return
     * @exception
     */ 
    @ConditionalOnMissingBean(SecurityManager.class)
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(accessRealm());
        securityManager.setCacheManager(cacheManager());
        securityManager.setRememberMeManager(rememberMeManager());
        //securityManager.setSessionManager(sessionManager);
        return securityManager;
    }
    
    /**  
     * <b>cacheManager</b>
     * <p>默认为EHcache的缓存管理器，可以用户自定已覆盖</p>
     * @return
     * @exception
     */ 
    @ConditionalOnMissingBean(CacheManager.class)
    @Bean
    public CacheManager cacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:config/ehcache.xml");
        return cacheManager;
    }
    
    /**
     * LifecycleBeanPostProcessor，这是个DestructionAwareBeanPostProcessor的子类，
     * 负责org.apache.shiro.util.Initializable类型bean的生命周期的，初始化和销毁。
     * 主要是AuthorizingRealm类的子类，以及EhCacheManager类。
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    
    
    @Bean
    public SimpleCookie rememberMeCookie(){
        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //如果httyOnly设置为true，则客户端不会暴露给客户端脚本代码，使用HttpOnly cookie有助于减少某些类型的跨站点脚本攻击；
        simpleCookie.setHttpOnly(true);
        //记住我cookie生效时间,默认30天 ,单位秒：60 * 60 * 24 * 30
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }

    /**
     * cookie管理器;
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager(){
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        //rememberme cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度（128 256 512 位），通过以下代码可以获取
        //KeyGenerator keygen = KeyGenerator.getInstance("AES");
        //SecretKey deskey = keygen.generateKey();
        //System.out.println(Base64.encodeToString(deskey.getEncoded()));
        byte[] cipherKey = Base64.decode("wGiHplamyXlVB11UXWol8g==");
        cookieRememberMeManager.setCipherKey(cipherKey);
        cookieRememberMeManager.setCookie(rememberMeCookie());
        return cookieRememberMeManager;
    }
    
    
    /*@ConditionalOnMissingBean(DefaultWebSessionManager.class)
    @Bean
    public DefaultWebSessionManager defaultWebSessionManager() {
        
        
    }*/
    
}
